/*-------------------------------------------------------------------------------
* file:     app_spi.c
* brief:    application
* copyright Copyright (C) 2020-2022 @ NanoIC
* Date:     2022-08-15
* Version:  V1.00
* Author:   JMH
*-------------------------------------------------------------------------------*/

/* include ----------------------------------------------------------------*/

#include "pin.h"
#include "app_spi.h"


/* Global variable -------------------------------------------------------------------*/
#define SPI_SEND_BUF_MAX_ELNGTH     260
#define SPI_READ_BUF_MAX_ELNGTH     260

uint8_t spi_send_buffer[SPI_SEND_BUF_MAX_ELNGTH + 8];
uint8_t spi_read_buffer[SPI_READ_BUF_MAX_ELNGTH + 8];
uint32_t spi_read_len;
volatile uint32_t spi_temp_reg_val;

const static short lock_cfg[] = {
    0x1C,         //0 ~ 0x3FFFF 256k 
    0x18,         //0 ~ 0x3FFFF 256k
    0x14,         //0 ~ 0x1FFFF 128k
    0x10,         //0 ~ 0x2FFFF 192k
    0x0C,         //0 ~ 0x37FFF 224k
    0x08,         //0 ~ 0x3BFFF 240K 
    0x04,         //0 ~ 0x3DFFF 248K
    0x00          //unlock
};

const static uint16_t flash_lock_lower_size[8] = 
{
    256,256,128,192,224,240,248,0x00
};

static uint16_t lock_ctl;
static uint8_t  need_unlock_flash;


#define SECTOR_ERASE_TIMEROUT               60000
#define FLASH_WRITE_STATUS_KEY              0X12345679

/*
 *  功能描述：初始化 SPI Flash相关IO
 *     PB0.8    ： SPI0_NSS  WN8012每发1 Byte就拉高CS，所以用GPIO来实现CS
 *     PB0.10   ： SPI0_MISO
 *     PA0.6    ： SPI0_MOSI
 *     PA0.8    ： SPI0_SCK
 */
void app_spi_master_init(void)
{  
    // CS:GPIO
    GPIOB0->CONHR = (GPIOB0->CONHR & ~(0xF << 4*(SPI0_PIN_NSS-8))) | (0x02 << 4*(SPI0_PIN_NSS-8));	// Output
    GPIOB0->PUDR  = ((GPIOB0->PUDR) & ~(0x03 << (SPI0_PIN_NSS*2))) | (0x01 << (SPI0_PIN_NSS*2));	// PullUp
    GPIOB0->DSCR  = (GPIOB0->DSCR  | (0x02ul << (2*SPI0_PIN_NSS)));									// Fast
    GPIOB0->SODR = (0x01ul << SPI0_PIN_NSS);    // CS = 1

    SYSCON->PCER0 = 0x10000;  // Bit[16]=1 SPI CLK Enable
    GPIOB0->CONHR =(GPIOB0->CONHR & ~(0xF << 4*(SPI0_PIN_MISO-8))) | (SPI0_PIN_AF_MISO << 4*(SPI0_PIN_MISO-8));
    GPIOA0->CONLR =(GPIOA0->CONLR & ~(0xF << 4*SPI0_PIN_MOSI)) | (SPI0_PIN_AF_MOSI << (4*SPI0_PIN_MOSI));
    GPIOA0->CONHR =(GPIOA0->CONHR & ~(0xF << 4*(SPI0_PIN_SCK-8))) | (SPI0_PIN_AF_SCK << 4*(SPI0_PIN_SCK-8));    

    /*
     *  SPI 时钟 = PCLK / (CPSDR * (1+SCR)) 
     */
    // Bit[15..8]: SCR = 5
    // Bit[7]: SPH = 0
    // Bit[6]: SP0 = 0
    // Bit[3..0]: DSS = 6(8bit)
    SPI0->CR0 = 0x507;

    // Bit[8]: LPTXOE = 1 (使能发送)
    // Bit[7]: LPMD = 0 (SPI普通模式)
    // Bit[6..4]: RXIFLSEL = 4 (接收FIFO >= 1/2)
    // Bit[3]: SOD = 0
    // Bit[2]: MS = 0 (主机模式)
    // Bit[1]: SSE = 0 (SPI 禁止)  1（SPI使能）
    // Bit[0]: LBM = 0 (正常)
    SPI0->CR1 = 0x12;

    // 时钟分频位,2 ~ 254
    SPI0->CPSR = 0x02; \

    // disable all interrupt   
    SPI0->IMSCR = 0x00;

    lock_ctl = 0xFF;
    need_unlock_flash = 0x00;
}

void Nop_delay(int num)
{
	for(int i=0;i<num;i++)
	{
        __asm volatile ("nop");
	}
}

/*
 *  功能描述：通过SPI输出一帧数据
 */
void app_spi_master_write(uint8_t *p_buf,uint32_t len)
{
    uint32_t send_len;
    GPIOB0->CODR = (0x01ul << SPI0_PIN_NSS); // CS = 0
    send_len = 0;
    for(send_len=0;send_len<len;send_len++)
    {        
        while((SPI0->SR & 0x03) == 0x00)
        { // 发送Fifo已满 而且 非空，等待
            for(uint8_t k=0;k<10;k++)
            {
                __asm volatile ("nop");			
            }
        }
        SPI0->DR = p_buf[send_len];
    }

    while((SPI0->SR & 0x10) == 0x10)
    { // SPI正在发送或接收，等待
        for(uint8_t k=0;k<10;k++)
        {
            __asm volatile ("nop");
        }
    }

    for(int i=0;i<16;i++)
    {  // 将Fifo中数据读空
        if((SPI0->SR & 0x04) == 0x04)
        { // 接收Fifo非空
            spi_temp_reg_val += SPI0->DR;
        }else{
            break;
        }
    }
    GPIOB0->SODR = (0x01ul << SPI0_PIN_NSS); // CS = 1
}

uint32_t app_spi_flash_read_status0(void)
{
    spi_send_buffer[0] = CMD_FLASH_READ_STATUS0;
    spi_send_buffer[1] = 0xff;
    spi_read_buffer[1] = 0xff;
    spi_read_len = app_spi_master_write_read(spi_send_buffer,2,spi_read_buffer,SPI_READ_BUF_MAX_ELNGTH);
    return spi_read_buffer[1];
}

/*
 *  功能描述：读取SPI Flash状态字
 */
uint32_t app_spi_flash_read_status(void)
{
    uint32_t rd_val1;    
    rd_val1 = app_spi_flash_read_status0();
    return (rd_val1);
}

/*
 *  功能描述：判断SPI Flash是否忙，并等待
 */
int app_spi_flash_wait_busy(uint32_t time_out)
{
    uint32_t flash_status;
    while(time_out --)
    {
        flash_status = app_spi_flash_read_status();
        if((flash_status & 0x01) == 0x00)
        {
            return 0;            
        }
        Nop_delay(3000);
    }
    return -1;
}

/**
 * @brief 写SPI Flash状态字
 * 
 */
void spi_flash_write_status_register(uint32_t reg_val)
{
    app_spi_flash_write_en();
    app_spi_flash_wait_busy(SECTOR_ERASE_TIMEROUT);

    spi_send_buffer[0] = CMD_FLASH_UNLOCK_STATUS;
    spi_send_buffer[1] = reg_val&0xFF;
    app_spi_master_write(spi_send_buffer,2);
    app_spi_flash_wait_busy(SECTOR_ERASE_TIMEROUT);
}

/*
 *  功能描述：通过SPI输出、并读取一帧数据
 */
uint32_t app_spi_master_write_read(uint8_t *p_wbuf,uint32_t wlen,uint8_t *p_rbuf,uint32_t max_rlen)
{
    uint32_t send_len,read_len,num,spi_data_reg;
    GPIOB0->CODR = (0x01ul << SPI0_PIN_NSS); // CS = 0
    send_len = 0;
    read_len = 0;

    for(int i=0;i<16;i++)
    {  // 将Fifo中残留数据读空
        if((SPI0->SR & 0x04) == 0x04)
        { // 接收Fifo非空
            spi_temp_reg_val += SPI0->DR;
        }else{
            break;
        }
    }

    for(send_len=0;send_len<wlen;send_len++)
    {        
        while((SPI0->SR & 0x03) == 0x00)
        { // 发送Fifo满 而且 非空，等待
            for(uint8_t k=0;k<10;k++)
            {
                __asm volatile ("nop");			
            }
        }
        SPI0->DR = p_wbuf[send_len];       
        for(num=0;num<3;num++)
        {  // Read Fifo
            if((SPI0->SR & 0x04) == 0x04)
            { // 接收Fifo非空,读取数据
                if(read_len < max_rlen)
                {
                    p_rbuf[read_len ++] = SPI0->DR;
                }
            }else{
                break;
            }
        }
    }   

    while((SPI0->SR & 0x10) == 0x10)
    { // SPI正在发送或接收，等待
        if((SPI0->SR & 0x04) == 0x04)
        { // 接收Fifo非空,读取数据
            if(read_len < max_rlen)
            {
                p_rbuf[read_len ++] = SPI0->DR;
            }
        }
    }

    for(num=0;num<16;num++)
    {  // Read Fifo
        if((SPI0->SR & 0x04) == 0x04)
        { // 接收Fifo非空
            spi_data_reg = SPI0->DR; // Buff满时也可将Fifo读空
            if(read_len < max_rlen)
            {
                p_rbuf[read_len ++] = spi_data_reg;
            }
        }
    }

    GPIOB0->SODR = (0x01ul << SPI0_PIN_NSS); // CS = 1
    __asm volatile ("nop");
    return read_len;
}

/*
 *  功能描述：读取SPI Flash的厂商和设备ID
 */
uint32_t app_spi_master_read_flash_manufacture_id(void)
{
    uint32_t ret;
    // GD25Q40:  Manufacturer ID(0xC8) , 
    spi_send_buffer[0] = CMD_FLASH_READ_MANUFAC;
    for(uint8_t i=0;i<3;i++)
    {
        spi_send_buffer[1+i] = 0x00;
    }
    spi_read_len = app_spi_master_write_read(spi_send_buffer,6,spi_read_buffer,SPI_READ_BUF_MAX_ELNGTH);
    ret = spi_read_buffer[4] + ((uint32_t)spi_read_buffer[5] << 8);
    return ret;
}

/*
 *  功能描述：读取SPI Flash的厂商和设备ID(8 Byte)
 */
uint32_t app_spi_master_read_flash_unique_id(uint8_t * pbuf)
{
    uint32_t i;
    spi_send_buffer[0] = CMD_FLASH_READ_UID;
    for(uint8_t i=0;i<4;i++)
    {
        spi_send_buffer[1+i] = 0x00;
    }
    spi_read_len = app_spi_master_write_read(spi_send_buffer,13,spi_read_buffer,SPI_READ_BUF_MAX_ELNGTH);
    for(i=0;i<8;i++)
    {
        pbuf[i] = spi_read_buffer[5+i];
    }
    return spi_read_len;
}

// SPI Flash write en
void app_spi_flash_write_en(void)
{
    // write en
    spi_send_buffer[0] = CMD_FLASH_WRITE_EN;
    spi_send_buffer[1] = 0x00;
    app_spi_master_write(spi_send_buffer,1);
}

// 擦去Flash一个扇区或块
int app_spi_flash_sector_or_block_erase(uint32_t addr,uint32_t erase_size)
{
    if((erase_size == FLASH_SECTOR_SIZE) || (erase_size == FLASH_BLOCK32_SIZE) || (erase_size == FLASH_BLOCK64_SIZE))
    {
        if ((addr % FLASH_SECTOR_SIZE) == 0) //align SECTOR size
        {
			uint32_t irq = csi_irq_save();
            app_spi_flash_write_en();
            if(erase_size == FLASH_SECTOR_SIZE)
            {
                spi_send_buffer[0] = CMD_FLASH_SECTOR_ERASE;
            }else if(erase_size == FLASH_BLOCK32_SIZE)
            {
                spi_send_buffer[0] = CMD_FLASH_BLOCK32_ERASE;
            }else if(erase_size == FLASH_BLOCK64_SIZE)
            {
                spi_send_buffer[0] = CMD_FLASH_BLOCK64_ERASE;
            }            
            spi_send_buffer[1] = ((addr >> 16) & 0xff);
            spi_send_buffer[2] = ((addr >> 8) & 0xff);
            spi_send_buffer[3] = (addr & 0xff);
			
			
			app_spi_master_write(spi_send_buffer,4);
            app_spi_flash_wait_busy(SECTOR_ERASE_TIMEROUT);
			csi_irq_restore(irq);

            return 0;
        }
    }
    return -1;
}


/**
 * @brief : 向SPI Flash中写入数据,注意一次写长度不要大于256, 注决写之前的擦除操作
 *
 * @param offset    : 要写入的数据的地址，注意256字节对齐
 * @param data      : 接收到的数据指针
 * @param length    : 接收到的数据长度
 * 
 * @return 实际写入的数据长度。  0：表示写入失败
 */
uint16_t app_spi_flash_page_program(uint32_t offset,uint8_t *pdata,uint16_t length)
{
    uint16_t i;
    if((offset & 0xff) > 0)
    {  // 地址不对齐，不写入
        return 0;
    }
    if(length > 256)
    {  // 长度大于 1 page
        return 0;
    }
	uint32_t irq = csi_irq_save();
    app_spi_flash_write_en();

    app_spi_flash_wait_busy(SECTOR_ERASE_TIMEROUT);
    // page program
    spi_send_buffer[0] = CMD_FLASH_PAGE_PROGRAM;
    spi_send_buffer[1] = ((offset >> 16) & 0xff);
    spi_send_buffer[2] = ((offset >> 8) & 0xff);
    spi_send_buffer[3] = (offset & 0xff);
    for(i=0;i<length;i++)
    {
        spi_send_buffer[4 + i] = *pdata++;
    }

    app_spi_master_write(spi_send_buffer,length+4);
    app_spi_flash_wait_busy(SECTOR_ERASE_TIMEROUT);
	csi_irq_restore(irq);
    return length;
}

/**
 * @brief : 从SPI Flash中读出数据,一次读出最多256 Byte
 *
 * @param offset    : 要读取数据的地址
 * @param data      : 接收到的数据指针
 * @param length    : 接收到的数据长度
 * 
 * @return 实际读取的数据长度。  0：表示读取失败
 */
uint16_t app_spi_flash_page_read(uint32_t offset,uint8_t *pdata,uint16_t length)
{
    uint16_t i;

    if(length > 256)
    {
        length = 256;
    }

	uint32_t irq = csi_irq_save();
    for(i=0;i<SPI_SEND_BUF_MAX_ELNGTH;i++)
    {
        spi_send_buffer[i] = 0xff;
    }
    // Read Data: 0x03
    spi_send_buffer[0] = CMD_FLASH_READ;
    spi_send_buffer[1] = ((offset >> 16) & 0xff);
    spi_send_buffer[2] = ((offset >> 8) & 0xff);
    spi_send_buffer[3] = (offset & 0xff);
    spi_read_len = app_spi_master_write_read(spi_send_buffer,(length+4),spi_read_buffer,SPI_READ_BUF_MAX_ELNGTH);

    for(i=0;i<length;i++)
    {
        *pdata++ = spi_read_buffer[i+4];
    }
	csi_irq_restore(irq);
    return length;
}

/*
 *  功能描述：判断SPI Flash是否忙
 */
int app_spi_flash_busy(void)
{
    uint32_t flash_status;
    flash_status = app_spi_flash_read_status();
    if((flash_status & 0x01) == 0x00)
    {
        return 0;            
    }else
    {
        return 1;
    }
}

/**
 * @brief : 向SPI Flash中写入数据,注意一次写长度不要大于256, 注决写之前的擦除操作
 *
 * @param offset    : 要写入的数据的地址，注意256字节对齐
 * @param data      : 接收到的数据指针
 * @param length    : 接收到的数据长度
 * 
 * @return 实际写入的数据长度。  0：表示写入失败
 */
uint16_t app_spi_flash_page_write(uint32_t offset,uint8_t *pdata,uint16_t length)
{
    uint16_t i;
    if((offset & 0xff) > 0)
    {  // 地址不对齐，不写入
        return 0;
    }
    // if(length > 256)
    // {  // 长度大于 1 page
    //     return 0;
    // }

    app_spi_flash_write_en();
    spi_send_buffer[0] = CMD_FLASH_PAGE_PROGRAM;
    spi_send_buffer[1] = ((offset >> 16) & 0xff);
    spi_send_buffer[2] = ((offset >> 8) & 0xff);
    spi_send_buffer[3] = (offset & 0xff);
    for(i=0;i<length;i++)
    {
        spi_send_buffer[4 + i] = *pdata++;
    }
    app_spi_master_write(spi_send_buffer,length+4);
    return length;
}

//--------------------------------------------------------------------
// Flash 锁相关操作

/*
 *  功能描述：设置锁标志
 */
uint32_t drv_flash_lock_config(FLASH_LOCK_AREA_E area)
{
    lock_ctl = lock_cfg[area];
    return 0;
}

/*
 *  功能描述：锁定相应区域的Flash
 */
void boot_flash_lock_flash(FLASH_LOCK_AREA_E area)
{
    uint32_t lock_status,lock_size,reg_val;

    reg_val = app_spi_flash_read_status0();
    lock_status =  reg_val & 0x1C;
    lock_size = 0;
    for(uint8_t i=0;i<8;i++)
    {
        if(lock_status == lock_cfg[i])
        {
            lock_size = flash_lock_lower_size[i];
            break;
        }
    }
    if((lock_size != FLASH_NEED_LOCK_SIZE) && (lock_ctl == lock_cfg[area]))
    {  
        reg_val &= 0x1C;
        reg_val |= 0x10;
        spi_flash_write_status_register(reg_val);
    }
    lock_ctl = 0xFF;
}

/**
 * @brief 解锁上锁区域
 * 
 * @param area 
 */
void boot_flash_unlock_flash(FLASH_LOCK_AREA_E area)
{ 
    uint32_t reg_val;

    reg_val = app_spi_flash_read_status0();
    if(lock_ctl == lock_cfg[area])
    {
        reg_val &= ~(0x1C);
        reg_val |= 0x00;
        spi_flash_write_status_register(reg_val);
    }
    lock_ctl = 0xFF;
}

/*
 *  功能描述：检查SPI Flash锁定区域
 */
uint32_t app_spi_flash_locked_size(void)
{
    uint32_t lock_status;
    uint16_t lock_size;

    lock_status = app_spi_flash_read_status0();
    lock_status &= 0x1C;
    lock_size = 0;

    for(uint8_t i=0;i<8;i++)
    {
        if(lock_status == lock_cfg[i])
        {
            lock_size = flash_lock_lower_size[i];
            break;
        }
    }
    return lock_size;
}


#if (APP_DEBUG_SPI_FLASH_ENABLE == 1)
// extern U32_T Flash_Read_Word(U32_T addr);
// volatile uint32_t flash_rd_addr,flash_wr_addr;
// volatile uint32_t  temp_rw_flash_buf[64];

/**
 * @brief : 用于测试SPI Flash的代码
 */
void app_spi_master_test(void)
{
    //--------------------------------------------------------------------
    // 擦去SPI Falsh中的出厂数据区域
    // app_spi_flash_sector_or_block_erase(0,FLASH_BLOCK64_SIZE);

    //--------------------------------------------------------------------
    // 将出厂数据拷贝OTA区域
    // app_spi_flash_sector_or_block_erase(FLASH_BLOCK64_SIZE,FLASH_BLOCK64_SIZE);

    // flash_rd_addr = 0x00;
    // flash_wr_addr = 0x10000;
    // for(uint32_t j=0;j<(4*60);j++)
    // {
    //     app_spi_flash_page_read(flash_rd_addr,(uint8_t *)&temp_rw_flash_buf[0],256);
    //     app_spi_flash_page_program(flash_wr_addr,(uint8_t *)&temp_rw_flash_buf[0],256);
    //     flash_rd_addr += 256;
    //     flash_wr_addr += 256;
    // }
    //--------------------------------------------------------------------
    //
}
#endif
